function hoverCell(cell) {
	highlightClue(["across",cell.getAttribute("across")]);
	highlightClue(["down",cell.getAttribute("down")]);
}

function unhoverCell(cell) {
	unhighlightClue(["across",cell.getAttribute("across")]);
	unhighlightClue(["down",cell.getAttribute("down")]);
}

function hoverClue(clue) {
	var clueNum = clue.getAttribute("across");
	if ( clueNum === undefined ) {
		var clueNum = clue.getAttribute("down");
		highlightPuzzle(["down",clueNum]);
	} else {
		highlightPuzzle(["across",clueNum]);
	}
}

function unhoverClue(clue) {
	var clueNum = clue.getAttribute("across");
	if ( clueNum === undefined ) {
		var clueNum = clue.getAttribute("down");
		unhighlightPuzzle(["down",clueNum]);
	} else {
		unhighlightPuzzle(["across",clueNum]);
	}
}

function highlightPuzzle(clue) {
	var xword = window.document.getElementById("xword");
	var cells = xword.getElementsByClassName("cell");
	for (var i=0; i<cells.length; i++) {
		if (cells[i].getAttribute(clue[0]) == clue[1]) {
			cells[i].className = cells[i].className.split(" ").concat("highlight").join(" ");
		}
	}
}

function unhighlightPuzzle(clue) {
	var xword = window.document.getElementById("xword");
	var cells = xword.getElementsByClassName("cell");
	for (var i=0; i<cells.length; i++) {
		if (cells[i].getAttribute(clue[0]) == clue[1]) {
			var classArr = cells[i].className.split(" ");
			for(var j = 0; j<classArr.length; j++) {
				if(classArr[j] == "highlight") {
					classArr.splice(j, 1);
				}
			}
			cells[i].className = classArr.join(" ");
		}
	}
}

function highlightClue(clue){
	var clueList = window.document.getElementById(clue[0]);
	var clues = clueList.getElementsByClassName("clue");
	for (var i=0; i<clues.length; i++) {
		if (clues[i].getAttribute(clue[0]) == clue[1]) {
			clues[i].className = clues[i].className.split(" ").concat("highlight").join(" ");
		}
	}
}

function unhighlightClue(clue){
	var clueList = window.document.getElementById(clue[0]);
	var clues = clueList.getElementsByClassName("clue");
	for (var i=0; i<clues.length; i++) {
		if (clues[i].getAttribute(clue[0]) == clue[1]) {
			var classArr = clues[i].className.split(" ");
			for(var j = 0; j<classArr.length; j++) {
				if(classArr[j] == "highlight") {
					classArr.splice(j, 1);
				}
			}
			clues[i].className = classArr.join(" ");
		}
	}
}

function renderPuzzle(puzzle) {
	var clueNum = 1;
	var clueCount = 0;
	puzzle.acrossClues = new Object();
	puzzle.downClues = new Object();

	var xword = window.document.getElementById("xword");
	var acrossElem = window.document.getElementById("across");
	var downElem = window.document.getElementById("down");

	for (var h=0; h < puzzle.height ; h++) {
		var row = window.document.createElement("tr");
		row.setAttribute("row", h);
		xword.appendChild(row);
		for (var w=0; w < puzzle.width; w++) {
			var td = window.document.createElement("td");
			td.setAttribute("row", h);
			td.setAttribute("column", w);
			row.appendChild(td);
			if (puzzle.solution[h][w] == null) {
				td.setAttribute("class", "blank");
			} else {
				var cell = window.document.createElement("div");
				cell.setAttribute("class","cell");
				td.appendChild(cell);
				//cell.appendChild(window.document.createTextNode("\u00a0"));
				var cellspan = window.document.createElement("span");
				cell.appendChild(cellspan);
				cellspan.appendChild(window.document.createTextNode(puzzle.solution[h][w]?puzzle.solution[h][w]:"\u00a0"));
				var newlabel = 0;
				if ( (w == 0) || (puzzle.solution[h][w-1] == null) ) {
					// starting a new across answer
					newlabel += 1;
					puzzle.acrossClues[clueNum] = puzzle.clues[clueCount++];
				}
				if ( (h == 0) || (puzzle.solution[h-1][w] == null) ) {
					// starting a new down answer
					newlabel += 2;
					puzzle.downClues[clueNum] = puzzle.clues[clueCount++];
				}
				if ( newlabel != 0 ) {
					var label = window.document.createElement("div");
					label.setAttribute("class", "cluenum");
					label.appendChild(window.document.createTextNode(clueNum));
					cell.appendChild(label);
				}
				if ( newlabel & 1 ) {
					cell.setAttribute("across", clueNum);
				} else {
					cell.setAttribute("across", cell.parentNode.previousSibling.firstChild.getAttribute("across"));
				}
				if ( newlabel & 2 ) {
					cell.setAttribute("down", clueNum);
				} else {
					// unfortunately awkward
					cell.setAttribute("down", cell.parentNode.parentNode.previousSibling.childNodes[w].firstChild.getAttribute("down"));
				}
				cell.setAttribute("onMouseOver", "hoverCell(this)");
				cell.setAttribute("onMouseOut", "unhoverCell(this)");
				if ( newlabel != 0 ) {
					clueNum++;
				}
			}
		}
	}

	for (var clue in puzzle.acrossClues) {
		var clueEntryNum = window.document.createElement("span");
		clueEntryNum.setAttribute("class", "cluenum");
		clueEntryNum.appendChild(window.document.createTextNode(clue));
		var clueEntryText = window.document.createElement("span");
		clueEntryText.setAttribute("class", "cluetext");
		clueEntryText.appendChild(window.document.createTextNode(puzzle.acrossClues[clue]));
		var clueEntry = window.document.createElement("div");
		clueEntry.setAttribute("class", "clue");
		clueEntry.setAttribute("across", clue);
		clueEntry.setAttribute("onMouseOver", "hoverClue(this)");
		clueEntry.setAttribute("onMouseOut", "unhoverClue(this)");
		clueEntry.appendChild(clueEntryNum);
		clueEntry.appendChild(clueEntryText);
		acrossElem.appendChild(clueEntry);
	}
	for (var clue in puzzle.downClues) {
		var clueEntryNum = window.document.createElement("span");
		clueEntryNum.setAttribute("class", "cluenum");
		clueEntryNum.appendChild(window.document.createTextNode(clue));
		var clueEntryText = window.document.createElement("span");
		clueEntryText.setAttribute("class", "cluetext");
		clueEntryText.appendChild(window.document.createTextNode(puzzle.downClues[clue]));
		var clueEntry = window.document.createElement("div");
		clueEntry.setAttribute("class", "clue");
		clueEntry.setAttribute("down", clue);
		clueEntry.setAttribute("onMouseOver", "highlightPuzzle([\"down\","+clue+"]);");
		clueEntry.setAttribute("onMouseOut", "unhighlightPuzzle([\"down\","+clue+"]);");
		clueEntry.appendChild(clueEntryNum);
		clueEntry.appendChild(clueEntryText);
		downElem.appendChild(clueEntry);
	}
}

function parsePUZ(puz) {
	puzzle = new Object();

	puzzle.width = puz.getByteAt(0x2c);
	puzzle.height = puz.getByteAt(0x2d);
	length = puzzle.width * puzzle.height;

	solution = puz.getBytesAt(0x34, length);
	puzzle.solution = new Array();

	count = 0;
	for (h=0; h<puzzle.height; h++) {
		puzzle.solution[h] = new Array();
		for (w=0; w<puzzle.width; w++) {
			if (solution[count] == 46) {  // ASCII 46 = . (period)
				puzzle.solution[h][w] = null;
			} else {
				puzzle.solution[h][w] = String.fromCharCode(solution[count]);
			}
			count++;
		}
	}

	offset = 0x34 + (2 * length);
	puzzle.title = puz.getNullTerminatedStringAt(offset);
	offset += puzzle.title.length + 1;
	puzzle.author = puz.getNullTerminatedStringAt(offset);
	offset += puzzle.author.length + 1;
	puzzle.copyright = puz.getNullTerminatedStringAt(offset);
	offset += puzzle.copyright.length + 1;

	puzzle.clues = new Array();
	for (numClues = puz.getShortAt(0x2e, 0); numClues > 0; numClues--) {
		clue = puz.getNullTerminatedStringAt(offset);
		puzzle.clues.push(clue);
		offset += clue.length + 1;
	}

	puzzle.notes = puz.getNullTerminatedStringAt(offset);
	offset += puzzle.notes.length + 1;

	renderPuzzle(puzzle);
}

function getPUZ() {
	BinaryAjax("jz131212.puz",
		function(HTTP) {
			parsePUZ(HTTP.binaryResponse);
		});
}
